package ru.bragnikita.utils.logexplorer.web;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.ViewAccessScoped;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.MenuModel;
import org.primefaces.model.UploadedFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.bragnikita.utils.logexplorer.logic.FileDownloadException;
import ru.bragnikita.utils.logexplorer.logic.FileSystemInterface;
import ru.bragnikita.utils.logexplorer.logic.execution.ExecException;
import ru.bragnikita.utils.logexplorer.logic.execution.ScriptRunningUtils;
import ru.bragnikita.utils.logexplorer.logic.mock.FileSystemModificationException;
import ru.bragnikita.utils.logexplorer.model.FilterRequest;
import ru.bragnikita.utils.logexplorer.model.FsItem;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;

/**
 * @author Nikita.Brazhnikov
 */
@Named("dir_view")
@ViewAccessScoped
public class DirectoryExplorerViewBean implements Serializable {

	private static final Logger logger = LoggerFactory.getLogger(DirectoryExplorerViewBean.class);

	@Inject
	private FileSystemInterface fileSystem;

	private List<FsItem> directoryViewModel;

	private List<FsItem> selectedItems;

	private FsItem singleItem;

	private List<FsItem> filtered;

	private boolean tryToViewInBrowser = true;

	private String currentDirectory = System.getProperty("user.dir");

	private boolean hasParent = true;

	private MenuModel tableContextMenu;

	private String selectedEncoding = Charset.defaultCharset().name();

	@PostConstruct
	public void init() {
		final String glassfishDomainDir = System.getProperty("com.sun.aas.instanceRoot");
		if (glassfishDomainDir != null) {
			final File logDir = new File(glassfishDomainDir, "logs");
			if (logDir.exists() && logDir.isDirectory())
				currentDirectory = logDir.getAbsolutePath();
		}
		if (currentDirectory == null)
			currentDirectory = System.getProperty("user.dir");
		logger.info("Initial directory is {}", currentDirectory);
		hasParent = new File(currentDirectory).getParent() != null;
		refresh();
		FsItem initialSelection = Iterables.getFirst(directoryViewModel, null);
		if (initialSelection != null)
			selectedItems = Lists.newArrayList(initialSelection);
	}

	public String getCurrentDirectory() {
		return currentDirectory;
	}

	public void setCurrentDirectory(String currentDirectory) {
		this.currentDirectory = currentDirectory;
	}

	public List<FsItem> getDirectoryViewModel() {
		return directoryViewModel;
	}

	public FileSystemInterface getFileSystem() {
		return fileSystem;
	}

	public boolean isHasParent() {
		return hasParent;
	}

	public boolean isTryToViewInBrowser() {
		return tryToViewInBrowser;
	}

	public List<FsItem> getSelectedItems() {
		return selectedItems;
	}

	public void setSelectedItems(List<FsItem> selectedItems) {
		this.selectedItems = selectedItems;
	}

	public void setTryToViewInBrowser(boolean tryToViewInBrowser) {
		this.tryToViewInBrowser = tryToViewInBrowser;
	}

	public void refresh() {
		filtered = null;
		final List<FsItem> reloaded = reload(currentDirectory);
		hasParent = new File(currentDirectory).getParent() != null;
		if (reloaded != null) {
			directoryViewModel = reloaded;
			selectedItems = Lists.newArrayList();
			logger.debug("Directory {} was reloaded", currentDirectory);
		}
	}

	public void selectDirectory() {
		final FsItem selectedItem = getSingleSelectedItem();
		if (selectedItem != null) {
			logger.debug("Item {} selected", selectedItem.getAbsolutePath());
			if (selectedItem.isDirectory()) {
				currentDirectory = selectedItem.getAbsolutePath();
				refresh();
			} else {
				//Ignore
			}
		}
	}

	public void delete() {
		delete(Collections.singletonList(getSingleItem()));
	}

	public void execute() {
		final FsItem selectedItem = getSingleItem();
		if (selectedItem != null) {
			if (ScriptRunningUtils.isExecutable(selectedItem.getAbsolutePath())) {
				logger.debug("Executable script is selected");
				try {
					ScriptRunningUtils.runAcyncOnWindows(selectedItem.getAbsolutePath());
					FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Async script started", "Async script started"));
				} catch (ExecException e) {
					logger.error("", e);
					final String errorString = e.getMessage();
					FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Script execution is failed", errorString));
				}
			}
			else logger.debug("Generic file, ignored");
		} else logger.debug("No selected executables");
	}


	public void goUp() {
		if (currentDirectory != null) {
			File dir = new File(currentDirectory);
			String newDir = dir.getParent();
			if (newDir != null)
				currentDirectory = newDir;
			refresh();
		}
	}

	public List<FsItem> getFiltered() {
		return filtered;
	}

	public void setFiltered(List<FsItem> filtered) {
		this.filtered = filtered;
	}

	public FsItem getSingleItem() {
		return singleItem;
	}

	public void setSingleItem(FsItem singleItem) {
		this.singleItem = singleItem;
	}

	public MenuModel getTableContextMenu() {
		return tableContextMenu;
	}

	public String getSelectedEncoding() {
		return selectedEncoding;
	}

	public void setSelectedEncoding(String selectedEncoding) {
		this.selectedEncoding = selectedEncoding;
	}

	public FsItem getSingleSelectedItem() {
		return selectedItems == null ? null : Iterables.getFirst(selectedItems, null);
	}


	private List<FsItem> reload(String targetDir) {
		FilterRequest request = new FilterRequest(targetDir);
		try {
			final List<FsItem> items = fileSystem.getFilesSet(request).getItems();
			logger.debug("{} items found is {}", items.size(), targetDir);
			return items;
		} catch (FileDownloadException e) {
			logger.error("Reloading failed", e);
			FacesContext.getCurrentInstance().
				 addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), e.getMessage()));
			return null;
		}
	}

	public void handleFileUpload(FileUploadEvent fue) {
		final UploadedFile uploadedFile = fue.getFile();
		final String filename = uploadedFile.getFileName();
		logger.debug("Uploading file {} into {}", filename, currentDirectory);
		try {
			fileSystem.uploadFile(currentDirectory, filename, uploadedFile.getInputstream());
			FacesMessage msg = new FacesMessage("Successful", filename + " is uploaded into " + currentDirectory);
			FacesContext.getCurrentInstance().addMessage(null, msg);
		} catch (FileSystemModificationException e) {
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Failed", e.getMessage());
			FacesContext.getCurrentInstance().addMessage(null, msg);
		} catch (IOException ignored) {
		} finally {
			refresh();
		}
	}

	public void deleteSelected() {
		delete(getSelectedItems());
	}

	public void delete(List<FsItem> itemsToDelete) {
		logger.debug("Request to delete file(s)");
		if (CollectionUtils.isNotEmpty(itemsToDelete)) {
			final List<String> pathToDelete = Lists.newArrayListWithExpectedSize(CollectionUtils.size(itemsToDelete));
			for (FsItem selectedItem : itemsToDelete) {
				pathToDelete.add(selectedItem.getAbsolutePath());
			}
			int countToDelete = pathToDelete.size();
			logger.debug("{} files will be deleted", countToDelete);
			try {
				int countOfDeleted = fileSystem.deleteFiles(pathToDelete);
				if (countOfDeleted < countToDelete) {
					final String errorString = String.format("%d files have been deleted, %d requested", countOfDeleted, countToDelete);
					logger.warn(errorString);
					FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_WARN, "Not all requested files have been deleted", errorString);
					FacesContext.getCurrentInstance().addMessage(null, msg);
				} else {
					logger.debug("{} files have been successfully deleted", countOfDeleted);
					final String infoString = countOfDeleted + " files have been deleted";
					FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, infoString, infoString);
					FacesContext.getCurrentInstance().addMessage(null, msg);
				}
			} catch (FileSystemModificationException e) {
				logger.error("Deletion failed", e);
				FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Failed", e.getMessage());
				FacesContext.getCurrentInstance().addMessage(null, msg);
			} finally {
				refresh();
			}
		} else {
			logger.debug("No selected files");
		}
	}

}
